/*
Federal University of Pernambuco - UFPE
Center of Informatics (Cin)

University of Pernambuco - UPE
Engenharia da Computa��o - Ecomp

This code was created in order to study the scalability
of the Multiobjective Evolutionary Algorithms in
problems with many conflicting objectives


 */

package hidra.qualityIndicator;

import hidra.core.util.Util;
import hidra.jmetal.core.Problem;

import java.util.ArrayList;
import java.util.List;

import jmetal.problems.DTLZ.DTLZ1;

/**
 * @author Elliackin Figueiredo
 * @email  emnf@cin.ufpe.br
 */
public class Diversity2 {



	private static hidra.qualityIndicator.util.MetricsUtil utils_; 

	public Diversity2() {
		utils_ = new hidra.qualityIndicator.util.MetricsUtil();
	}

	public double diversity(double[][] front, int numDiv, Problem problem){

		double sumMetric = 0.0;
		double metric = 0;

		if(front.length == 1){
			return 0.0;
		}


		double valueObj = this.determineValueA(front, problem.getNumberOfObjectives(), problem); 	
		List<double[]> points = new ArrayList<double[]>();
		boolean flag = false;

		for(int i=0; i<front.length; i++){
			flag = true;
			for(int j=0; j  < front[i].length ; j++){

				if(front[i][j] > valueObj){
					flag = false;
				}

			}

			if(flag){
				points.add(front[i]);
			}
		}

		double[][] newFront = new double[points.size()][problem.getNumberOfObjectives()];

		for(int i=0; i < points.size() ; i++){
			newFront[i] = points.get(i);
		}


		if(newFront.length == 0 || newFront.length == 1)
			return 0.0;


		for(int obj=0; obj < problem.getNumberOfObjectives() ;obj++){					
			double[] h = new double[numDiv + 2];			
			double delta = valueObj; 										

			delta = delta / numDiv;

			if(delta != 0){			
				for(int j=0;  j < newFront.length ; j++){
					int index = 
							((int)((newFront[j][obj])/delta));

					h[index+1] = 1;   
				}				
				h[0] = 1;
				h[numDiv + 1] = 1;				
				double sum = 0;
				double value = 0.0;				
				for(int i=1; i <= newFront.length ; i++){
					value = this.lookupTable(h, i);
					sum = sum + value;
				}				
				metric = (sum)/ (newFront.length);				
			}else{
				metric= 0;
			}

			sumMetric += metric;
		}

		sumMetric = sumMetric/problem.getNumberOfObjectives();

		return sumMetric;
	}



	private double determineValueA(double[][] front, int numObj, Problem problem){

		double distance = 0.0;
		double min = Double.POSITIVE_INFINITY;

		for(int i=0; i< front.length; i++){			
			distance = Util.normEucleadian(front[i], numObj , 2);			
			min = Math.min(min, distance);		
		}


		if(problem instanceof DTLZ1){
			return min/Math.sqrt(2);
		}else{
			return min;
		}


	}




	private double lookupTable(double[] h, int i){

		if(h[i-1] == 0 && h[i] == 0 && h[i+1] == 0){
			return 0;
		}else if(h[i-1] == 0 && h[i] == 1 && h[i+1] == 0){
			return 0.75;
		}else if(h[i-1] == 1 && h[i] == 0 && h[i+1] == 1){
			return 0.75;
		}else if(h[i-1] == 1 && h[i] == 0 && h[i+1] == 0){
			return 0.50;
		}else if(h[i-1] == 0 && h[i] == 0 && h[i+1] == 1){
			return 0.50;
		}else if(h[i-1] == 0 && h[i] == 1 && h[i+1] == 1){
			return 0.67;
		}else if(h[i-1] == 1 && h[i] == 1 && h[i+1] == 0){
			return 0.67;
		}else{
			return 1;
		}

	}


}
